#include <iostream>

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/cstdint.hpp>


#include <google/protobuf/descriptor.h>

#include "generated/pepe.pb.h"

using boost::asio::io_service;
using boost::asio::ip::udp;
using boost::bind;

io_service io;
udp::socket sock(io, udp::endpoint(udp::v4(), 55555));
udp::endpoint sender_endpoint;
static const int max_length = 100;
char data[max_length];

PersonService_Stub pss(NULL);
const google::protobuf::ServiceDescriptor* sd(PersonService_Stub::descriptor());

void handle_incoming_data(const boost::system::error_code& error,
  size_t bytes_recvd);

::google::protobuf::Message* prepareReply() {
    uint32_t index = reinterpret_cast<uint32_t*>(data)[1];
    uint16_t msgType = reinterpret_cast<uint16_t*>(data)[1];
    const google::protobuf::MethodDescriptor* md = 
      sd->method(msgType);
    google::protobuf::Message* response = pss.GetResponsePrototype(md).New();
    PhoneNumber* pn = dynamic_cast<PhoneNumber*>(response);
    pn->set_number("1242325");
    pn->set_type(PhoneNumber_PhoneType_MOBILE);
    return pn;
}

void handle_send(const boost::system::error_code& error,
  size_t bytes_sent) {
    std::cout << "Sent!" << std::endl;
        sock.async_receive_from(
          boost::asio::buffer(data, max_length), sender_endpoint,
          bind(handle_incoming_data,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));
}

void handle_incoming_data(const boost::system::error_code& error,
  size_t bytes_recvd) {
    if (!error && bytes_recvd > 0)
    {
        std::cout << "Got packet... sending back" << std::endl;
        google::protobuf::Message* reply = prepareReply();
        reply->SerializeToArray(data, reply->ByteSize());

        sock.async_send_to(
          boost::asio::buffer(data, reply->ByteSize()), sender_endpoint,
          bind(handle_send,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));
    }
    else
    {
        sock.async_receive_from(
          boost::asio::buffer(data, max_length), sender_endpoint,
          bind(handle_incoming_data,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));
    }
}

int main() {
    sock.async_receive_from(boost::asio::buffer(data, max_length), sender_endpoint, bind(handle_incoming_data,
        boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
    io.run();
}
